import { onMounted, onUnmounted } from 'vue'
import * as THREE from 'three';
import Stats from 'three/examples/jsm/libs/stats.module';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { Water } from 'three/examples/jsm/objects/Water';
import { Sky } from 'three/examples/jsm/objects/Sky';
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { CSS2DObject, CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'

import { CarInfoType } from '../types'
import { CarModelsType } from '../types';

let camera: THREE.PerspectiveCamera
let scene: THREE.Scene
let renderer: THREE.WebGLRenderer
let controls: OrbitControls
let labelRenderer: CSS2DRenderer
let water: Water
let sun: THREE.Vector3
let sky: Sky
let container: HTMLElement | null
let stats: any;
let pmremGenerator: THREE.PMREMGenerator
let renderTarget: THREE.WebGLRenderTarget

let carModels: CarModelsType = { car1: null, car2: null, car3: null };

let modelsReady = 0
let timer = 0

let carGroup: THREE.Group

const linePosition = {
	0: {
		bigin: {
			x: -1.5,
			y: 22.4,
			z: -320
		},
		mid: {
			x: -1.5,
			y: 22.4,
			z: -130
		},
		end: {
			x: -1.5,
			y: 22.4,
			z: 60
		}
	},
	1: {
		bigin: {
			x: 1.5,
			y: 22.4,
			z: 60
		},
		mid: {
			x: 1.5,
			y: 22.4,
			z: -130
		},
		end: {
			x: 1.5,
			y: 22.4,
			z: -320
		}
	}
}
const CarHashMap = {
	car1: {
		黄: 'url(./chepai/小型车黄牌.png)',
		绿: 'url(./chepai/小型车绿牌.png)',
		蓝: 'url(./chepai/小型车蓝牌.png)',
	},
	car2: {
		黄: 'url(./chepai/中型车黄牌.png)',
		绿: 'url(./chepai/中型车绿牌.png)',
		蓝: 'url(./chepai/中型车蓝牌.png)',
	},
	car3: {
		黄: 'url(./chepai/大型车黄牌.png)',
		绿: 'url(./chepai/大型车绿牌.png)',
		蓝: 'url(./chepai/大型车蓝牌.png)',
	}
}

let parameters:any;

export function init(p:any, container:HTMLElement) {

	parameters = p;

	timer = setInterval(() => {
		updateSun()
	
	}, 1000)

	initScene(container);

	onUnmounted(() => {
		clear()
	})

}



export function initScene(container:HTMLElement) {

	carGroup = new THREE.Group()

	// 渲染器
	renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(container.offsetWidth, container.offsetHeight);
	renderer.toneMapping = THREE.ACESFilmicToneMapping;
	renderer.shadowMap.enabled = true
	renderer.shadowMap.type = THREE.VSMShadowMap

	container.appendChild(renderer.domElement);

	pmremGenerator = new THREE.PMREMGenerator(renderer);

	// 2d渲染器
	labelRenderer = new CSS2DRenderer()
	labelRenderer.setSize(container.offsetWidth, container.offsetHeight);
	labelRenderer.domElement.style.position = 'absolute';
	labelRenderer.domElement.style.top = '0';
	labelRenderer.domElement.style.pointerEvents = 'none';
	container.appendChild(labelRenderer.domElement)

	// 场景
	scene = new THREE.Scene();
	scene.add(carGroup)

	// 相机
	camera = new THREE.PerspectiveCamera(55, container.offsetWidth / container.offsetHeight, 1, 20000);
	camera.position.set(0, 40, -20);

	// 水 和 太阳
	initSunWater();

	// 相机轨道控制器
	controls = new OrbitControls(camera, renderer.domElement);
	controls.maxPolarAngle = Math.PI * 0.495;
	// controls.enablePan = false
	controls.target.set(0, 10, -100);
	controls.maxDistance = 2000.0;
	controls.update();

	// 性能监视器
	stats = Stats();
	let statsDom = stats.dom;
	statsDom.style.position = 'absolute';
	container.appendChild(statsDom);

	// loader
	const dracoLoader = new DRACOLoader()
	dracoLoader.setDecoderPath("./draco/")

	const loader = new GLTFLoader()
	loader.setDRACOLoader(dracoLoader)

	// 桥
	loader.load('./models/high_bridge.glb', gltf => {

		const model = gltf.scene
		model.scale.set(0.1, 0.1, 0.1)
		modelsReady += 1

		model.traverse((e: any) => {
			if (e.isMesh) {
				e.castShadow = true
				e.receiveShadow = true
				e.geometry.computeVertexNormals()
			}
		})

		model.rotation.y = Math.PI / 2
		model.position.y -= 3

		const model1 = model.clone()
		model1.position.z -= 259
		model1.position.y -= 0.1
		model1.rotation.y = -Math.PI / 2

		scene.add(model1)
		scene.add(model)

		// 开启渲染
		animate()

	})

	// 车
	loader.load('./models/cars.glb', gltf => {

		const model = gltf.scene.children[0]

		model.traverse((e: any) => {
			if (e.isMesh) {
				e.castShadow = true
				e.receiveShadow = true
				e.geometry.computeVertexNormals()
			}
		})

		carModels.car1 = model.children[1]
		carModels.car2 = model.children[2]
		carModels.car3 = model.children[3]

		carModels.car1.scale.set(30, 30, 30)
		carModels.car2.scale.set(30, 30, 30)
		carModels.car3.scale.set(30, 30, 30)

		modelsReady += 1
	})

	// 大飞机
	loader.load('./models/plane.glb', gltf => {

		const model = gltf.scene.children[0]

		model.traverse((e: any) => {
			if (e.isMesh) {
				e.castShadow = true
				e.receiveShadow = true
				e.geometry.computeVertexNormals()
			}
		})

		model.scale.set(0.001, 0.001, 0.001)
		model.position.set(0, 60, -300)
		scene.add(model)

	})

	window.addEventListener('resize', onWindowResize);
	window.addEventListener('mousedown', onWindowMousedown)

}

function onWindowResize() {

	if (container) {

		camera.aspect = container.offsetWidth / container.offsetHeight;

		camera.updateProjectionMatrix();

		renderer.setSize(container.offsetWidth, container.offsetHeight);

	}

}

function onWindowMousedown(event: MouseEvent) {

	const raycaster = new THREE.Raycaster();
	const pointer = new THREE.Vector2();

	pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
	pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;

	raycaster.setFromCamera(pointer, camera)

	const intersect = raycaster.intersectObjects(carGroup.children) as any

	if (intersect.length > 0) {

		const flag = intersect[0].object.parent.getObjectByName('label').visible

		intersect[0].object.parent.getObjectByName('label').visible = !flag;

	}
}

function animate() {

	requestAnimationFrame(animate);
	render();
	stats.update();

}

function render() {

	water.material.uniforms['time'].value += 1.0 / 60.0;

	THREE && TWEEN.update()

	renderer.render(scene, camera);
	labelRenderer.render(scene, camera);

}

function initSunWater() {
	// 太阳
	sun = new THREE.Vector3();

	// 水
	const waterGeometry = new THREE.PlaneGeometry(10000, 10000);

	water = new Water(
		waterGeometry,
		{
			textureWidth: 512,
			textureHeight: 512,
			waterNormals: new THREE.TextureLoader().load('./textures/waternormals.jpg', function (texture) {

				texture.wrapS = texture.wrapT = THREE.RepeatWrapping;

			}),
			sunDirection: new THREE.Vector3(),
			sunColor: 0xffffff,
			waterColor: 0x001e0f,
			distortionScale: 3.7,
			fog: scene.fog !== undefined
		}
	);

	water.rotation.x = - Math.PI / 2;

	scene.add(water);

	// 天空
	sky = new Sky();
	sky.scale.setScalar(10000);
	scene.add(sky);

	const skyUniforms = sky.material.uniforms;

	skyUniforms['turbidity'].value = 10;
	skyUniforms['rayleigh'].value = 2;
	skyUniforms['mieCoefficient'].value = 0.002;  // 太阳光晕大小
	skyUniforms['mieDirectionalG'].value = 0.8;

	updateSun()
}

function updateSun() {
	if (!sun) return
	let deg = 90 - parameters.elevation;           // 太阳的角度
	const phi = THREE.MathUtils.degToRad(deg);
	const theta = THREE.MathUtils.degToRad(parameters.azimuth);

	sun.setFromSphericalCoords(1, phi, theta);

	sky.material.uniforms['sunPosition'].value.copy(sun);
	water.material.uniforms['sunDirection'].value.copy(sun).normalize();

	if (renderTarget !== undefined) renderTarget.dispose();

	renderTarget = pmremGenerator.fromScene(sky as any);

	scene.environment = renderTarget.texture;

}

export function getCar(ins: CarInfoType) {
	// console.log(ins);
	if (modelsReady >= 2) {
		beginTraffic(ins);
	}

}

function beginTraffic(ins: CarInfoType) {

	const car = (carModels[ins.type] as THREE.Object3D).clone() as THREE.Group
	const pos = linePosition[ins.line].bigin
	car.position.set(pos.x, pos.z, pos.z)
	if (ins.line === 1) car.rotation.y = Math.PI

	const position = {
		x: linePosition[ins.line].bigin.x,
		y: linePosition[ins.line].bigin.y,
		z: linePosition[ins.line].bigin.z,
	}
	let pointLabel: CSS2DObject
	const tween1 = new TWEEN.Tween(position)

	tween1.to(linePosition[ins.line].mid, ins.middleTimeStamp - ins.beginTimeStamp)

	tween1.onStart(() => {

		const img = CarHashMap[ins.type][ins.color]
		const innerHTML = ins.licence
		let labelDiv = createDiv(img, innerHTML)

		pointLabel = new CSS2DObject(labelDiv);
		pointLabel.visible = false
		pointLabel.name = 'label'

		car.add(pointLabel)

		carGroup.add(car)

	})

	tween1.onUpdate(() => {

		car.position.x = position.x
		car.position.y = position.y
		car.position.z = position.z

	})

	const tween2 = new TWEEN.Tween(position)

	tween2.to(linePosition[ins.line].end, ins.endTimeStamp - ins.middleTimeStamp)

	tween2.onUpdate(() => {

		car.position.x = position.x
		car.position.y = position.y
		car.position.z = position.z

	})

	tween2.onComplete(() => {
		car.remove(pointLabel)
		carGroup.remove(car)
	})

	tween1.chain(tween2).start()

}



function createDiv(imgUrl: string, innerHTML: string) {

	const labelDiv = document.createElement("div");
	labelDiv.style.width = '163px'
	labelDiv.style.height = '50px'
	labelDiv.style.backgroundImage = imgUrl
	labelDiv.style.textAlign = 'center'
	labelDiv.style.verticalAlign = 'center'
	labelDiv.style.color = '#ffffff'
	labelDiv.style.borderRadius = '25px'

	const div = document.createElement('div')
	div.innerHTML = innerHTML
	div.style.fontSize = '14px'
	div.style.width = '113px'
	div.style.marginLeft = '40px'
	div.style.height = '25px'
	div.style.lineHeight = '50px'


	labelDiv.appendChild(div)


	return labelDiv
}

function clear() {

	window.removeEventListener('resize', onWindowResize)
	window.removeEventListener('mousedown', onWindowMousedown)
	document.body.removeChild(labelRenderer.domElement)

	scene.traverse((child: any) => {

		if (child.isMesh) {
			if (child.geometry) child.geometry.dispose();
			if (child.material) child.material.dispose();
			child.clear();
		}
	});

	clearInterval(timer)

	TWEEN && TWEEN.removeAll()

	scene.clear();

	renderer.dispose();

	renderer.forceContextLoss();

	THREE.Cache.clear();

}


